package server;

import java.util.ArrayList;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import server.console.ConsoleHandler;
import server.net.io.Acceptor;
import server.net.io.Dispatcher;
import server.net.io.ServerIOHandler;

/**
 * This represents the game server. It is responsible for accepting and handling
 * requests from clients. It does so by delegating these tasks to an Acceptor, a
 * pool of Dispatchers, and a ServerHandler.
 *
 * Acceptor-Dispatcher pattern adapted from an article by Gregor Roth:
 * Architecture of a Highly Scalable NIO-Based Server
 * < http://today.java.net/pub/a/today/2007/02/13/architecture-of-highly-scalable-nio-server.html >
 * 
 * @author Vincent Tsuei
 * @version 11-20-2012
 */
public class GameServer implements Runnable {

    //Status variables
    private boolean shutdown = false;
    private int portNumber;
    
    //IO stuff
    private Acceptor acceptor;
    private ArrayList<Dispatcher> dispatchers;
    private ServerIOHandler handler;
    
    // Registries
    private PlayerRegistry playerRegistry;
    private ServerRegistry gameRegistry;
    
    //Scheduling/Timer stuff
    private ScheduledThreadPoolExecutor scheduler;

    //Create the GameServer object, but don't run or initialize anything yet.
    public GameServer(int Port) {
        portNumber = Port;
    }

    @Override
    public void run() {
        int coresAvail = Runtime.getRuntime().availableProcessors();
        
        //Create Registries.
        playerRegistry = new PlayerRegistry(this);
        gameRegistry = new ServerRegistry(this);

        //Create the scheduler. This thread pool handles tasks that need to
        //be executed at a certain time, and not necessarily immediately.
        scheduler = new ScheduledThreadPoolExecutor(Math.max(coresAvail / 2, 1), new ThreadFactory() {
            private final ThreadGroup tGroup = new ThreadGroup("SchedulerThreadGroup");
            private int threadCounter = 1;

            //It should be okay to use synchronized here, we shouldn't be creating many threads.
            @Override
            public synchronized Thread newThread(Runnable r) {
                return new Thread(tGroup, r, "SchedulerThread-" + (threadCounter++));
            }
        }); //TODO: Consider adding RejectedExecutionHandler (for debugging)
        
        //Create the ServerHandler. This will handle the actual incoming and
        //outgoing messages.
        handler = new ServerIOHandler();
        handler.init();

        //Create the dispatcher pool. Dispatchers aggregate multiple connections
        //and wait for read/writes in a non-blocking manner. We will have a
        //static amount of dispatchers, the amount of which will depend on the
        //number of available cores.
        dispatchers = new ArrayList<Dispatcher>();
        for (int i = 1; i < Math.max(coresAvail / 2, 2) + 1; i++) {
            Dispatcher newDispatch = new Dispatcher(this, i);
            dispatchers.add(newDispatch);
            new Thread(newDispatch).start();
        }

        //Create an acceptor on the previously specified port.
        acceptor = new Acceptor(this, portNumber);
        new Thread(acceptor, "AcceptorThread").start();

        //TODO: Server Console
        if (ConsoleHandler.ConsoleAvail()) {
            ConsoleHandler.RunConsole();
        }
    }

    /**
     * Get the ServerHandler for this game server.
     *
     * @return the associated ServerHandler
     */
    public ServerIOHandler getHandler() {
        return handler;
    }

    /**
     * Get the Dispatcher that is handling the least amount of clients at the
     * moment. This is not guaranteed to produce the best load-balancing
     * solution, but should serve as a useful approximation.
     *
     * @return the dispatcher with the least load
     */
    public Dispatcher getLeastLoadDispatcher() {
        Dispatcher leastLoadDispatcher = null;
        int minLoad = Integer.MAX_VALUE;
        for (Dispatcher d : dispatchers) {
            int load = d.getSelectorKeyCount();
            if (load < minLoad) {
                minLoad = load;
                leastLoadDispatcher = d;
            }
        }
        return leastLoadDispatcher;
    }

    /**
     * Tells us if the server is shutting down. TODO: Properly implement the
     * shutdown sequence. (To be figured out later)
     *
     * @return true if the server is in the process of shutting down, false
     * otherwise.
     */
    public boolean isShuttingDown() {
        return shutdown;
    }
    
    /**
     * Gives access to the scheduler for this server.
     * 
     * Note: This may be too broad access, and we may wish to limit access
     * by introducing an interface. However, for the purposes of this
     * project, this is fine.
     */
    public ScheduledThreadPoolExecutor getScheduler() {
        return scheduler;
    }
    
    /**
     * Get the player registry of this server.
     */
    public PlayerRegistry getPlayerRegistry()
    {
        return playerRegistry;
    }
    
    /**
     * Get the server registry of this server.
     */
    public ServerRegistry getServerRegistry()
    {
        return gameRegistry;
    }
    
}
